package com.hcss.common.util.excel.util;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;

import org.apache.commons.beanutils.BeanUtils;

/**
 * @Description: Bean转换位Map<br>
 *               这个BeanUtil转为Excel提供服务
 * @Author:momo
 * @Since:2018年12月21日
 * @Version:1.1.0
 */
public class ExcelBeanUtil {
    private static final String CLASS = "class";

	/**
	 * 判断是否为Bean对象<br>
	 * 判定方法是是否存在只有一个参数的setXXX方法
	 * 
	 * @param clazz
	 *            待测试类
	 * @return 是否为Bean对象
	 */
	public static boolean isBean(Class<?> clazz) {
		if (isNormalClass(clazz)) {
			final Method[] methods = clazz.getMethods();
			for (Method method : methods) {
				if (method.getParameterTypes().length == 1 && method.getName().startsWith("set")) {
					// 检测包含标准的setXXX方法即视为标准的JavaBean
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * 是否为标准的类<br>
	 * 这个类必须： 1、非接口 2、非抽象类 3、非Enum枚举 4、非数组 5、非注解 6、非原始类型（int, long等）
	 * 
	 * @param clazz
	 * @return 是否为标准类
	 */
	public static boolean isNormalClass(Class<?> clazz) {
		return null != clazz //
				&& false == clazz.isInterface() && false == Modifier.isAbstract(clazz.getModifiers())
				&& false == clazz.isEnum() && false == clazz.isArray() && false == clazz.isAnnotation()
				&& false == clazz.isSynthetic() && false == clazz.isPrimitive();
	}

	/**
	 * Bean转换为Map
	 * 
	 * @param bean
	 * @return
	 */
	public static Map<?, ?> beanToMap(Object bean, Set<String> set) {
		try {
			Class<? extends Object> type = bean.getClass();
			Map<String, String> returnMap = new LinkedHashMap<String, String>();
			BeanInfo beanInfo = Introspector.getBeanInfo(type);
			PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
			for (String field : set) { // 这个是为了保证顺序
				for (int i = 0; i < propertyDescriptors.length; i++) {
					PropertyDescriptor descriptor = propertyDescriptors[i];
					String propertyName = descriptor.getName();
					if (!CLASS.equals(propertyName) && propertyName.equals(field)) {
						Method readMethod = descriptor.getReadMethod();
						Object result = readMethod.invoke(bean );
						if (result != null) {
							returnMap.put(propertyName, result.toString());
						}else {
							returnMap.put(propertyName, "");
						}
					}
				}
			}
			return returnMap;
		} catch (Exception e) {
			return null;
		}
	}

	/**
	 * 将一个 Map 对象转化为一个 JavaBean
	 * 
	 * @param type 要转化的类型
	 * @param map 包含属性值的 map
	 * @return 转化出来的 JavaBean 对象
	 */
	public static <T> T transMap2Bean2(Map<String, Object> map, Class<T> clazz) {
		T obj = null;
		if (map == null) {
			return null;
		}
		try {
			obj = clazz.newInstance();
			BeanUtils.populate(obj, map);
		} catch (Exception e) {
			System.out.println("transMap2Bean2 Error " + e);
		}
		return obj;
	}
	
	/**
     * 将Map集合中的数据封装到JavaBean对象中
     * @param map        集合
     * @param classType 封装javabean对象
     * @throws Exception 
     * 
     * TODO
     */
    public static <T> T map2bean(Map<String, Object> map,Class<T> classType) throws Exception {
        //采用反射动态创建对象
        T obj = classType.newInstance();
        //获取对象字节码信息,不要Object的属性
        BeanInfo beanInfo = Introspector.getBeanInfo(classType,Object.class);
        //获取bean对象中的所有属性
        PropertyDescriptor[] list = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor pd : list) {
            String key = pd.getName();    //获取属性名
            Object value=map.get(key);  //获取属性值
            pd.getWriteMethod().invoke(obj, value);//调用属性setter()方法,设置到javabean对象当中
        }
        return obj;
    }
	
    
	// 下边这个方法异常好多···
//	public static <T> T mapToBean(Class<T> beanClass, Map<?, ?> map) throws Exception {
//		BeanInfo beanInfo = Introspector.getBeanInfo(beanClass); // 获取类属性
//		T obj = beanClass.newInstance(); // 创建 JavaBean 对象 ,但是我发现一个问题，如果这里使用的是内部类，就无法创建了，会抛异常
//
//		// 给 JavaBean 对象的属性赋值
//		PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
//		for (int i = 0; i < propertyDescriptors.length; i++) {
//			PropertyDescriptor descriptor = propertyDescriptors[i];
//			String propertyName = descriptor.getName();
//
//			if (map.containsKey(propertyName)) {
//				// 下面一句可以 try 起来，这样当一个属性赋值失败的时候就不会影响其他属性赋值。
//				Object value = map.get(propertyName);
//
//				Object[] args = new Object[1];
//				args[0] = value;
//
//				descriptor.getWriteMethod().invoke(obj, args);
//			}
//		}
//		return obj;
//	}

	/**
	 * 实例化对象
	 * 
	 * @param <T>
	 *            对象类型
	 * @param clazz
	 *            类
	 * @param params
	 *            构造函数参数
	 * @return 对象
	 * @throws UtilException
	 *             包装各类异常
	 */
	@SuppressWarnings("unchecked")
	public static <T> T newInstance(Class<T> clazz, Object... params) throws Exception {
		if (null == params || params.length == 0) {
			if (Map.class.isAssignableFrom(clazz)) {
				// Map
				if (LinkedHashMap.class.isAssignableFrom(clazz)) {
					return (T) new LinkedHashMap<>();
				} else {
					return (T) new HashMap<>();
				}
			} else if (Iterable.class.isAssignableFrom(clazz)) {
				// Iterable
				if (LinkedHashSet.class.isAssignableFrom(clazz)) {
					return (T) new LinkedHashSet<>();
				} else if (Set.class.isAssignableFrom(clazz)) {
					return (T) new HashSet<>();
				} else if (LinkedList.class.isAssignableFrom(clazz)) {
					return (T) new LinkedList<>();
				} else {
					return (T) new ArrayList<>();
				}
			}

			final Constructor<T> constructor = getConstructor(clazz);
			try {
				return constructor.newInstance();
			} catch (Exception e) {
				throw new Exception("Instance class error!");
			}
		}

		final Class<?>[] paramTypes = getClasses(params);
		final Constructor<T> constructor = getConstructor(clazz, paramTypes);
		if (null == constructor) {
			throw new Exception("No Constructor matched for parameter types");
		}
		try {
			return constructor.newInstance(params);
		} catch (Exception e) {
			throw new Exception("Instance class error!");
		}
	}

	/**
	 * 获得对象数组的类数组
	 * 
	 * @param objects
	 *            对象数组，如果数组中存在{@code null}元素，则此元素被认为是Object类型
	 * @return 类数组
	 */
	public static Class<?>[] getClasses(Object... objects) {
		Class<?>[] classes = new Class<?>[objects.length];
		Object obj;
		for (int i = 0; i < objects.length; i++) {
			obj = objects[i];
			classes[i] = (null == obj) ? Object.class : obj.getClass();
		}
		return classes;
	}

	/**
	 * 查找类中的指定参数的构造方法，如果找到构造方法，会自动设置可访问为true
	 * 
	 * @param <T>
	 *            对象类型
	 * @param clazz
	 *            类
	 * @param parameterTypes
	 *            参数类型，只要任何一个参数是指定参数的父类或接口或相等即可，此参数可以不传
	 * @return 构造方法，如果未找到返回null
	 */
	@SuppressWarnings("unchecked")
	public static <T> Constructor<T> getConstructor(Class<T> clazz, Class<?>... parameterTypes) {
		if (null == clazz) {
			return null;
		}

		final Constructor<?>[] constructors = clazz.getDeclaredConstructors();
		Class<?>[] pts;
		for (Constructor<?> constructor : constructors) {
			pts = constructor.getParameterTypes();
			if (isAllAssignableFrom(pts, parameterTypes)) {
				// 构造可访问
				constructor.setAccessible(true);
				return (Constructor<T>) constructor;
			}
		}
		return null;
	}

	/**
	 * 比较判断types1和types2两组类，如果types1中所有的类都与types2对应位置的类相同，或者是其父类或接口，则返回<code>true</code>
	 * 
	 * @param types1
	 *            类组1
	 * @param types2
	 *            类组2
	 * @return 是否相同、父类或接口
	 */
	public static boolean isAllAssignableFrom(Class<?>[] types1, Class<?>[] types2) {
		if ((null == types1 || types1.length == 0) && (null == types2 || types2.length == 0)) {
			return true;
		}
		if (null == types1 || null == types2) {
			// 任何一个为null不相等（之前已判断两个都为null的情况）
			return false;
		}
		if (types1.length != types2.length) {
			return false;
		}

		Class<?> type1;
		Class<?> type2;
		for (int i = 0; i < types1.length; i++) {
			type1 = types1[i];
			type2 = types2[i];
			if (isBasicType(type1) && isBasicType(type2)) {
				// 原始类型和包装类型存在不一致情况
				if (BasicType.unWrap(type1) != BasicType.unWrap(type2)) {
					return false;
				}
			} else if (false == type1.isAssignableFrom(type2)) {
				return false;
			}
		}
		return true;
	}

	/**
	 * 是否为基本类型（包括包装类和原始类）
	 * 
	 * @param clazz
	 *            类
	 * @return 是否为基本类型
	 */
	public static boolean isBasicType(Class<?> clazz) {
		if (null == clazz) {
			return false;
		}
		return (clazz.isPrimitive() || isPrimitiveWrapper(clazz));
	}

	/**
	 * 是否为包装类型
	 * 
	 * @param clazz
	 *            类
	 * @return 是否为包装类型
	 */
	public static boolean isPrimitiveWrapper(Class<?> clazz) {
		if (null == clazz) {
			return false;
		}
		return BasicType.WRAPPERPRIMITIVEMAP.containsKey(clazz);
	}

}
